package com.simple.indiapay.service.impl;

import cn.hutool.core.util.IdUtil;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.simple.common.entity.ResData;
import com.simple.common.entity.constant.Payment;
import com.simple.common.entity.dto.pay.india.V3PayDto;
import com.simple.common.entity.dto.pay.india.V3PayRspDto;
import com.simple.common.entity.dto.pay.vm.QueryTradeDto;
import com.simple.common.entity.dto.pay.vm.QueryTradeRspDto;
import com.simple.common.entity.po.sys.ChannelComp;
import com.simple.common.entity.po.sys.Trade;
import com.simple.common.framework.retrofit.ExecuteUtils;
import com.simple.common.framework.retrofit.Retrofits;
import com.simple.indiapay.api.PayApi;
import com.simple.indiapay.config.PayConfig;
import com.simple.indiapay.service.PayService;
import lombok.extern.slf4j.Slf4j;
import okhttp3.ResponseBody;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import retrofit2.Call;
import retrofit2.Retrofit;

import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;

import static com.simple.common.entity.constant.Payment.TRADE_STATUS_DESCRIBE.EVEN_SERIAL_STATUS_PAYMENT_PENDING_MSG;

/**
 * @author yasud
 * @date 2021年11月05日 22:41
 * @description
 */
@Service
@Slf4j
public class PayServiceImpl extends BaseService implements PayService {

    @Autowired
    PayConfig config;

    /**
     * 支付下单
     *
     * @param dto
     * @return
     */
    @Override
    public V3PayRspDto pay(V3PayDto dto) {
        Trade trade = dto.getTrade();
        ChannelComp channel = dto.getChannel();
        String apiHost = channel.getAsString("apiHost");
        Map<String, Object> map = generatePayParam(trade, channel, dto.getExtra());
        Retrofit retrofit = Retrofits.SINGLE.createRetrofit(apiHost);
        PayApi payApi = retrofit.create(PayApi.class);
        Call<ResponseBody> call = payApi.pay(map);
        Map<String, Object> result = ExecuteUtils.execute(call);
        log.info("订单号[{}]渠道支付下单请求成功，响应结果-->{}", trade.getId(), gson.toJson(result));
        V3PayRspDto rspDto = new V3PayRspDto();
        String channelStatus = (String) result.get("status");
        if (!"success".equalsIgnoreCase(channelStatus)) {
            log.info("订单号[{}]渠道支付下单失败，原因-->{}", trade.getId(), channelStatus + ":" + result.get("msg"));
            String msg = (String) result.getOrDefault("msg", "未知错误");
            trade.setMsg(Payment.TRADE_STATUS_DESCRIBE.EVEN_SERIAL_STATUS_PAYMENT_FAILURE_MSG);
            trade.setChannelMsg(msg);
            trade.setStatus(Trade.Status.FAILED);
            trade.setChannelStatus(channelStatus);
        } else {
            Map<String, Object> data = (Map<String, Object>) result.get("data");
            log.info("订单号[{}]渠道支付下单成功，支付路径-->{}", trade.getId(), data.get("payurl"));
            String orderid = "" + data.get("orderid");
            trade.setChannelOrderNo(orderid);
            trade.setStatus(Trade.Status.PENDING);
            trade.setMsg(EVEN_SERIAL_STATUS_PAYMENT_PENDING_MSG);
            trade.setChannelStatus(channelStatus);
            trade.setChannelMsg(EVEN_SERIAL_STATUS_PAYMENT_PENDING_MSG);
            trade.setChannelOrderNo(orderid);
            rspDto.setPaymentUrl((String) data.get("payurl"));
        }
        rspDto.setTrade(trade);
        return rspDto;
    }

    /**
     * 构建支付请求参数
     *
     * @param trade
     * @param channel
     * @param extra
     * @return
     */
    private Map<String, Object> generatePayParam(Trade trade, ChannelComp channel, Map<String, Object> extra) {
        Map<String, Object> req = Maps.newHashMap();
        req.put("pay_memberid", channel.getMid());
        req.put("pay_orderid", trade.getId().toString());
        req.put("pay_userid", RandomStringUtils.randomAlphanumeric(20));
        req.put("pay_applydate", DateFormatUtils.format(trade.getCreateTime(), "yyyy-MM-dd HH:mm:ss"));
        req.put("pay_bankcode", channel.getAsString("bankcode"));
        req.put("pay_notifyurl", config.getPlatformBasePath() + "/api/receiver/dynamic/call/" + trade.getId());
        req.put("pay_callbackurl", MessageFormat.format(config.getPlatformBasePath() + "/api/pay/redirect/{0}/{1}", channel.getChannelFlag(), trade.getId() + ""));
        req.put("pay_amount", trade.getPayAmount());
        req.put("pay_productname", extra.get("orderTitle"));
        req.put("pay_attach", extra.getOrDefault("remark", ""));
        req.put("pay_md5sign", md5(req, channel.getSecretKey(), Sets.newHashSet("pay_productname", "pay_attach")));
        log.info("订单号[{}]渠道支付下单请求参数-->{}", trade.getId(), gson.toJson(req));
        return req;
    }

    /**
     * 订单状态查询
     *
     * @param dto
     * @return
     */
    @Override
    public QueryTradeRspDto queryTrade(QueryTradeDto dto) {
        Trade trade = dto.getTrade();
        ChannelComp channel = dto.getChannel();
        String apiHost = channel.getAsString("apiHost");
        Map<String, Object> map = generateQueryParam(trade, channel);
        Retrofit retrofit = Retrofits.SINGLE.createRetrofit(apiHost);
        PayApi payApi = retrofit.create(PayApi.class);
        Call<ResponseBody> call = payApi.query(map);
        Map<String, Object> result = ExecuteUtils.execute(call);
        log.info("订单号[{}]渠道支付结果查询参数，响应结果-->{}", trade.getId(), gson.toJson(result));
        String channelReqStatus = (String) result.get("returncode");
        String transactionId = (String) result.get("transaction_id");
        String msg = (String) result.getOrDefault("msg", "未知错误");
        if (!"00".equalsIgnoreCase(channelReqStatus)) {
            trade.setMsg(Payment.TRADE_STATUS_DESCRIBE.EVEN_SERIAL_STATUS_PAYMENT_FAILURE_MSG);
            trade.setChannelMsg(msg);
            trade.setStatus(Trade.Status.FAILED);
            trade.setChannelStatus(channelReqStatus);
            trade.setChannelOrderNo(transactionId);
        } else {
            String channelStatus = (String) result.get("trade_state");
            if ("SUCCESS".equalsIgnoreCase(channelStatus)) {
                trade.setStatus(Trade.Status.PENDING);
                trade.setMsg(EVEN_SERIAL_STATUS_PAYMENT_PENDING_MSG);
                trade.setChannelStatus(channelStatus);
                trade.setChannelMsg(EVEN_SERIAL_STATUS_PAYMENT_PENDING_MSG);
                trade.setChannelOrderNo(transactionId);
            } else if ("NOTPAY".equalsIgnoreCase(channelStatus)) {
                trade.setMsg(EVEN_SERIAL_STATUS_PAYMENT_PENDING_MSG);
                trade.setChannelMsg(EVEN_SERIAL_STATUS_PAYMENT_PENDING_MSG);
                trade.setStatus(Trade.Status.PENDING);
                trade.setChannelStatus(channelReqStatus);
                trade.setChannelOrderNo(transactionId);
            } else {
                trade.setMsg(Payment.TRADE_STATUS_DESCRIBE.EVEN_SERIAL_STATUS_PAYMENT_FAILURE_MSG);
                trade.setChannelMsg(msg);
                trade.setStatus(Trade.Status.FAILED);
                trade.setChannelStatus(channelReqStatus);
                trade.setChannelOrderNo(transactionId);
            }
            trade.setChannelStatus(channelStatus);
            trade.setChannelMsg(channelStatus);
        }
        QueryTradeRspDto rspDto = new QueryTradeRspDto();
        rspDto.setTrade(trade);
        log.info("订单号[{}]渠道支付结果查询完成，订单支付结果-->{}", trade.getId(), trade.getStatus().name());
        return rspDto;
    }


    /**
     * 构建订单查询请求参数
     *
     * @param trade
     * @param channel
     * @return
     */
    private Map<String, Object> generateQueryParam(Trade trade, ChannelComp channel) {
        Map<String, Object> req = Maps.newHashMap();
        req.put("pay_memberid", channel.getMid());
        req.put("pay_orderid", trade.getId().toString());
        req.put("pay_md5sign", md5(req, channel.getSecretKey(), null));
        log.info("订单号[{}]渠道支付结果查询参数-->{}", trade.getId(), gson.toJson(req));
        return req;
    }
}
